/*
类的多态
多态分为两类：
  静态多态：函数重载 和 运算符重载属于静态多态，复用函数名
  动态多态：派生类 和 虚函数 实现运行时多态
静态多态和动态多态的区别：
  静态多态的函数地址早绑定 - 编译阶段确定函数地址
  动态多态的函数地址晚绑定 - 运行阶段确定函数地址

虚函数， 在函数前加关键字 virtual 函数就变成了虚函数。那么编译器在编译阶段就不能确定函数调用了。

多态满足条件：
  1.有继承关系
  2.子类重写父类中的虚函数 （函数返回值类型 函数名 参数列表 完全一致称为重写）

多态使用, 父类指针或函数引用指向子类对象

C++开发提倡利用多态设计程序架构，因为多态优点很多
多态优点：代码组织结构清晰，可读性强，利于前期和后期的扩展以及维护

在多态中，通常父类中虚函数的实现是毫无意义的，主要都是调用子类重写的内容，因此父类中的虚函数可以改为纯虚函数
纯虚函数语法： virtual 返回值类型 函数名 (参数列表) = 0;
当类中有纯虚函数，这个类也成为 抽象类
抽象类的特点：
  无法实例化对象
  子类必须重写抽象类中的纯虚函数，否则也属于抽象类

在使用多态是，如果子类中有属性开辟到堆区，那么父指针在释放时无法调用到子类中的析构代码
解决方式：将父类中的析构函数改成虚析构或纯虚析构
虚析构和纯虚析构共性：
  可以解决父类指针释放子类对象
  都需要有具体的函数实现
虚析构和纯虚析构区别：
  如果是纯虚析构，该类属于抽象类，无法实例化对象
虚析构语法： virtual ~类名 () {}
纯虚析构语法：
  virtual ~类名 () = 0;
  类名::~类名(){}
*/

#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
    virtual void speak()
    {
        cout << "The animal is speaking..." << endl;
    }

    // 析构函数加上virtual关键字，变成虚析构函数
    // virtual ~Animal()
    //{
    //	cout << "Animal虚析构函数调用！" << endl;
    // }

    virtual ~Animal() = 0; // 纯虚析构  有纯虚函数的类是抽象类
};
Animal::~Animal()
{
    cout << "call ~Animal..." << endl;
}

class Cat : public Animal
{
public:
    void speak()
    {
        cout << "The cat is speaking..." << endl;
    }

    Cat() {};

    Cat(string name)
    {
        cout << "Call Cat(string name)..." << endl;
        m_name = new string(name);
    }

    ~Cat()
    {
        cout << "Call ~Cat()..." << endl;
        if (this->m_name != NULL)
        {
            delete this->m_name;
            this->m_name = NULL;
        }
    }

public:
    string *m_name;
};

class Dog : public Animal
{
public:
    void speak()
    {
        cout << "The dog is speaking..." << endl;
    }
};

// 我们希望传入什么对象，那么就调用什么对象的函数
// 如果函数地址在编译阶段就能确定，那么静态联编
// 如果函数地址在运行阶段才能确定，就是动态联编
void do_speak(Animal &animal)
{
    animal.speak();
}

// 纯虚函数
class Base
{
public:
    // 纯虚函数
    // 类中只要有一个纯虚函数就称为抽象类
    // 抽象类无法实例化对象
    // 子类必须重写父类中的纯虚函数，否则也属于抽象类
    virtual void func() = 0;
};

class Son : public Base
{
public:
    void func()
    {
        cout << "Calling func..." << endl;
    }
};

int main()
{
    // Cat cat;
    // do_speak(cat);

    Dog dog;
    do_speak(dog);

    // 纯虚函数测试
    Base *base = NULL;
    // base = new Base; // 错误，抽象类无法实例化对象
    base = new Son;
    base->func();
    delete base; // 调用完记得销毁

    // 虚析构测试
    cout << "-------------" << endl;
    Animal *animal = new Cat("Tom");
    animal->speak();
    delete animal; // 用完要删除
    cout << "-------------" << endl;

    return 0;
}